hotels <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-02-11/hotels.csv')

When you finish the assignment, remove the # from the options chunk at the top, so that messages and warnings aren’t printed. If you are getting errors in your code, add error = TRUE so that the file knits. I would recommend not removing the # until you are completely finished.

Put it on GitHub!

From now on, GitHub should be part of your routine when doing assignments. I recommend making it part of your process anytime you are working in R, but I’ll make you show it’s part of your process for assignments.

Task: When you are finished with the assignment, post a link below to the GitHub repo for the assignment.

Here is my GitHub repo link: https://github.com/ducminhngo871/Machine-Learninng-Lasso.

Machine Learning review and intro to tidymodels

Read through and follow along with the Machine Learning review with an intro to the tidymodels package posted on the Course Materials page.

Tasks:

  1. Read about the hotel booking data, hotels, on the Tidy Tuesday page it came from. There is also a link to an article from the original authors. The outcome we will be predicting is called is_canceled.
  • Without doing any analysis, what are some variables you think might be predictive and why?

Without doing any analysis, I think previous_cancellations, previous_bookings_not_canceled and deposit_type might be the three variables that might be predictive. The reason for that is becasue previous_cancellations show the number of previous bookings that were cancelled by the customer prior to the current booking. The same thing can be applied for previous_bookings_not_canceled.

Additionally, for deposit_type, if the room has been charged (Non Refund), it will be more likely that the room will not get cancelled a lot of time.

_ What are some problems that might exist with the data? You might think about how it was collected and who did the collecting.

Some problems might exist with the data are:

  • As the data was obtained directly from the hotels’ PMS databases’ servers, the hotel’s workers were the one who complete the data. Therefore, it might be biased based on the view of the hotel’s workers.

    • If we construct a model, what type of conclusions will be able to draw from it?

If we create a model, we can find which variables have the highest impact on the decision of cancelling a booking or not.

  1. Create some exploratory plots or table summaries of the variables in the dataset. Be sure to also examine missing values or other interesting values. You may want to adjust the fig.width and fig.height in the code chunk options.
library(tidyverse)         # for reading in data, graphing, and cleaning
library(tidymodels)        # for modeling ... tidily
library(usemodels)         # for suggesting step_XXX() functions
library(glmnet)            # for regularized regression, including LASSO
library(naniar)            # for examining missing values (NAs)
library(lubridate)         # for date manipulation
library(moderndive)        # for King County housing data
library(vip)               # for variable importance plots
library(rmarkdown)         # for paged tables
theme_set(theme_minimal()) # my favorite ggplot2 theme :)

First, I will see the first 5 rows of the data to see its variables:

hotels %>% 
  slice(1:5)

In here, I can see the dataset has 32 variables and the one we will focus on in this assignment is is_canceled: Value indicating if the booking was canceled (1) or not (0).

1. Quick exploration

After seeing the dataset for a brief moment, I will take a quick look at distributions of all the variables to check for anything irregular.

Quantitative variables:

hotels %>% 
  select(where(is.numeric)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_histogram(bins = 30) +
  facet_wrap(vars(variable), 
             scales = "free")

When I see the histogram plots of the variables, I can see that the data was collected for 3 years with the majority of data comes from 2016 and 2017.

Things I have noticed from the graphs:

  • Right skewness in lead_time, stays_in_week_nights –> log transform if using linear regression.

  • Many 0’s in children, days_in_waiting_list, adr,adults,is_repeated_guest and required_car_parking_spaces –> create indicator variables of having that feature vs. not, ie. a variable called children where a 0 indicates no basement (children = 0) and a 1 indicates a basement (children > 0).

For the categorical variables:In this dataset, there are no categorical variables.

Additionally, we might want to see the number of cancelled booking versus the number of booking not being cancelled.

hotels %>% 
  count(is_canceled)

From here, we can see that the number of bookings that got canceled is close to the number of bookings that did not get canceled. The number of data is sufficient enough for us to do any machine learning models.

  1. First, we will do a couple things to get the data ready.
  • I did the following for you: made outcome a factor (needs to be that way for logistic regression), made all character variables factoors, removed the year variable and some reservation status variables, and removed cases with missing values (not NULLs but true missing values).

  • You need to split the data into a training and test set, stratifying on the outcome variable, is_canceled. Since we have a lot of data, split the data 50/50 between training and test. I have already set.seed() for you. Be sure to use hotels_mod in the splitting.

hotels_mod <- hotels %>% 
  mutate(is_canceled = as.factor(is_canceled)) %>% 
  mutate(across(where(is.character), as.factor)) %>% 
  select(-arrival_date_year,
         -reservation_status,
         -reservation_status_date) %>% 
  add_n_miss() %>% 
  filter(n_miss_all == 0) %>% 
  select(-n_miss_all)
set.seed(494)

# Randomly assigns 75% of the data to training.
hotels_split <- initial_split(hotels_mod, 
                             prop = .5)
hotels_split
## <Analysis/Assess/Total>
## <59693/59693/119386>
#<training/testing/total>

hotels_training <- training(hotels_split)
hotels_testing <- testing(hotels_split)
  1. In this next step, we are going to do the pre-processing. Usually, I won’t tell you exactly what to do here, but for your first exercise, I’ll tell you the steps.
  • Set up the recipe with is_canceled as the outcome and all other variables as predictors (HINT: ~.).
  • Use a step_XXX() function or functions (I think there are other ways to do this, but I found step_mutate_at() easiest) to create some indicator variables for the following variables: children, babies, and previous_cancellations. So, the new variable should be a 1 if the original is more than 0 and 0 otherwise. Make sure you do this in a way that accounts for values that may be larger than any we see in the dataset.
  • For the agent and company variables, make new indicator variables that are 1 if they have a value of NULL and 0 otherwise. I also used step_mutate_at() for this, but there’s more ways you could do it.
  • Use fct_lump_n() inside step_mutate() to lump together countries that aren’t in the top 5 most occurring.
  • If you used new names for some of the new variables you created, then remove any variables that are no longer needed.
  • Use step_normalize() to center and scale all the non-categorical predictor variables. (Do this BEFORE creating dummy variables. When I tried to do it after, I ran into an error - I’m still investigating why.)
  • Create dummy variables for all factors/categorical predictor variables (make sure you have -all_outcomes() in this part!!).
  • Use the prep() and juice() functions to apply the steps to the training data just to check that everything went as planned.
hotel_recipe <- recipe(is_canceled ~ .,
                       data = hotels_training) %>% 
  step_mutate_at(children, babies, previous_cancellations,
                 fn = ~ as.numeric(. > 0)) %>%
  step_mutate_at(agent, company,
                 fn = ~ as.numeric(. == "NULL")) %>%
  step_mutate(country, 
              country_grp = fct_lump_n(country, n = 5)) %>% 
  step_rm(country) %>% 
  step_normalize(all_predictors(),
                 -all_nominal(),
                 -all_outcomes()) %>%
  step_dummy(all_nominal(),
             -all_outcomes())
hotel_recipe %>% 
  prep(hotels_training) %>%
  # using bake(new_data = NULL) gives same result as juice()
  # bake(new_data = NULL)
  juice() 
  1. In this step we will set up a LASSO model and workflow.
  • In general, why would we want to use LASSO instead of regular logistic regression? (HINT: think about what happens to the coefficients).

  • Lasso regularizations are also known as ‘shrinkage’ methods, because they reduce or shrink the coefficients in the resulting regression. This reduces the variance in the model: as input variables are changed, the model’s prediction changes less than it would have without the regularization. While using LASSO model, we can avoid overfit by reducing the variance of a model.

  • Meanwhile, logistic regression does not shrink the variables, so we will have a lot of variables than needed.

  • Define the model type, set the engine, set the penalty argument to tune() as a placeholder, and set the mode.

  • Create a workflow with the recipe and model.

hotel_lasso_mod <- 
  # Define a lasso model 
  logistic_reg(mixture = 1) %>% 
  # Set the engine to "glmnet" 
  set_engine("glmnet") %>% 
  # The parameters we will tune.
  set_args(penalty = tune()) %>% 
  # Use "regression"
  set_mode("classification")
## Set up the workflow: 
hotel_lasso_wf <- 
  # Set up the workflow
  workflow() %>% 
  # Add the recipe
  add_recipe(hotel_recipe) %>% 
  # Add the modeling
  add_model(hotel_lasso_mod)

hotel_lasso_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 6 Recipe Steps
## 
## • step_mutate_at()
## • step_mutate_at()
## • step_mutate()
## • step_rm()
## • step_normalize()
## • step_dummy()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
## 
## Main Arguments:
##   penalty = tune()
##   mixture = 1
## 
## Computational engine: glmnet
  1. In this step, we’ll tune the model and fit the model using the best tuning parameter to the entire training dataset.
  • Create a 5-fold cross-validation sample. We’ll use this later. I have set the seed for you.
  • Use the grid_regular() function to create a grid of 10 potential penalty parameters (we’re keeping this sort of small because the dataset is pretty large). Use that with the 5-fold cv data to tune the model.
  • Use the tune_grid() function to fit the models with different tuning parameters to the different cross-validation sets.
  • Use the collect_metrics() function to collect all the metrics from the previous step and create a plot with the accuracy on the y-axis and the penalty term on the x-axis. Put the x-axis on the log scale.
  • Use the select_best() function to find the best tuning parameter, fit the model using that tuning parameter to the entire training set (HINT: finalize_workflow() and fit()), and display the model results using pull_workflow_fit() and tidy(). Are there some variables with coefficients of 0?
set.seed(494) # for reproducibility

#Split the data into 5-fold: 
hotel_cv <- vfold_cv(hotels_training, v = 5)

# potential penalty parameters
penalty_grid <- grid_regular(penalty(),
                             levels = 10)

hotel_lasso_tune <- 
  hotel_lasso_wf %>% 
  tune_grid(
    resamples = hotel_cv,
    grid = penalty_grid
  )
collect_metrics(hotel_lasso_tune, summarize = TRUE)
### How can you select only "accuracy" not roc_curve. 
collect_metrics(hotel_lasso_tune) %>%  
  filter(.metric == "accuracy") %>% 
  ggplot(aes(x = log(penalty), y = mean)) + geom_point()

hotel_lasso_tune %>% 
  show_best(metric = "accuracy")
# Best tuning parameter by accuracy
best_param <- hotel_lasso_tune %>% 
  select_best(metric = "accuracy")
best_param
hotel_lasso_final_wf <- hotel_lasso_wf %>% 
  finalize_workflow(best_param)
hotel_lasso_final_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 6 Recipe Steps
## 
## • step_mutate_at()
## • step_mutate_at()
## • step_mutate()
## • step_rm()
## • step_normalize()
## • step_dummy()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
## 
## Main Arguments:
##   penalty = 1e-10
##   mixture = 1
## 
## Computational engine: glmnet

Now we could fit this to the training data and look at the resulting model.

hotel_lasso_final_mod <- hotel_lasso_final_wf %>% 
  fit(data = hotels_training)

hotel_lasso_final_mod %>% 
  pull_workflow_fit() %>% 
  tidy() 

In here, I can see a few terms have coefficients of 0 (such as arrival_date_month_September, market_segment_Undefined, distribution_channel_Undefined)

  1. Now that we have a model, let’s evaluate it a bit more. All we have looked at so far is the cross-validated accuracy from the previous step.
  • Create a variable importance graph. Which variables show up as the most important? Are you surprised?
# Visualize variable importance
hotel_lasso_final_mod %>% 
  pull_workflow_fit() %>% 
  vip()

In here, the reserved_room_type_P comes up as the most important features, followed by assigned_room_type_I and deposit_type_Non.Refund. For me personally, I am pretty surprised about the variable importance. Since I thought that deposit type should be the most important variables, however, I am fairly surprise that it is only a top 3 categories. Moreover, I thought previous_cancellations might be an important variable, however, I did not see that in the model.

  • Use the last_fit() function to fit the final model and then apply it to the testing data. Report the metrics from the testing data using the collect_metrics() function. How do they compare to the cross-validated metrics?
# Fit model with best tuning parameter(s) to training data and apply to test data
hotel_lasso_test <- hotel_lasso_final_wf %>% 
  last_fit(hotels_split)

# Metrics for model applied to test data
hotel_lasso_test %>% 
  collect_metrics()
# This one is not totally right. 
collect_metrics(hotel_lasso_tune) %>%
  filter(.metric == 'accuracy')

In here, when we look at the accuracy rate, the testing data’s accuracy is fairly consistent with the training data. For the testing data, it is estimated that the accuracy is 0.8138140, slightly lower than the training data (0.8161091). However, the number is fairly consistent.

  • Use the collect_predictions() function to find the predicted probabilities and classes for the test data. Save this to a new dataset called preds. Then, use the conf_mat() function from dials (part of tidymodels) to create a confusion matrix showing the predicted classes vs. the true classes. Compute the true positive rate (sensitivity), true negative rate (specificity), and accuracy. See this Wikipedia reference if you (like me) tend to forget these definitions. Also keep in mind that a “positive” in this case is a cancellation (those are the 1’s).
preds <- collect_predictions(hotel_lasso_test)
preds
preds %>%
  conf_mat(truth = is_canceled, estimate = .pred_class)
##           Truth
## Prediction     0     1
##          0 34221  7734
##          1  3380 14358

From this confusion matrix:

  • The true positive rate will be: \[\frac{34221}{34221+ 7734} = \frac{34221}{41955} = 0.816\]

  • The true negative rate will be: \[\frac{14358}{14358 + 3380} = \frac{14358}{17738} = 0.809\]

  • The accuracy will be: \[\frac{34221 + 14358}{34221 + 7734 + 3380 + 14358} = 0.8138 \]

  • Use the preds dataset you just created to create a density plot of the predicted probabilities of canceling (the variable is called .pred_1), filling by is_canceled. Use an alpha = .5 and color = NA in the geom_density().

preds %>%
  ggplot(aes(x = .pred_1, fill = is_canceled)) + geom_density(alpha = 0.5, color = NA)

Answer these questions:

  1. What would this graph look like for a model with an accuracy that was close to 1?

I believe that a model with an accuracy that was close to 1 will have two distinct parts and will not have many overlap points.Moreover, the two parts will be really left-skewed and right-skewed. The reason for that will be: if the dataset said that this person canceled his booking (is_canceled = 1) and the dataset correctly predict that, the prediction will be close to 1. With that, there will have less dataset with .pred_1 < 0.5 while is_canceled = 1 and vice versa for is_canceled = 0. With that, the graph will be split into two parts: one right-skewed red part and one left-skewed blue part.

  1. Our predictions are classified as canceled if their predicted probability of canceling is greater than .5. If we wanted to have a high true positive rate, should we make the cutoff for predicted as canceled higher or lower than .5?

As the true positive rate can be calculated as True Positive / (True Positive + False Negative). Therefore, to make the true positive rate higher, we should make the cutoff higher, which then makes the prediction more accurate.

  1. What happens to the true negative rate if we try to get a higher true positive rate?

If we try to get a higher true positive rate, we need to increase the cut off rate. As we have less postive cases, we will have more negative cases. With that, we will be more likely to get a false negative cases and the true negative rate will decrease.

  1. Let’s say that this model is going to be applied to bookings 14 days in advance of their arrival at each hotel, and someone who works for the hotel will make a phone call to the person who made the booking. During this phone call, they will try to assure that the person will be keeping their reservation or that they will be canceling in which case they can do that now and still have time to fill the room. How should the hotel go about deciding who to call? How could they measure whether it was worth the effort to do the calling? Can you think of another way they might use the model?

For the model, according to the importance variables graph, the hotel should call the person who books the room type P or has been assigned to room I. Additionally, they should call the person who books the room is not refundable.

They could measure who to call based on the rate of that room was being cancelled as well as how likely that will happen. For example, they can look into the variable importance to decide which variables will be important. Once they combine their previous undertanding about the demographic as well as the result from the model, they can find out who should they call.

Another way they can use the model is that they can focus more on some key parts (for example: on the reserved_room P, G or F rather than A,B or C). With these models and its attributions to the cancellation number, they can implement the variables that have positive attributes such as lead_time or stay_in_week_nights for instance to create new rules. For example, they can lower the rate to stay in the hotel during week nights so that they can attract more visitors during that time.

  1. How might you go about questioning and evaluating the model in terms of fairness? Are there any questions you would like to ask of the people who collected the data?

In here, since the person who creates the data works in the hotel, one might ask whether he/she collects the data accurately or not. If he/she lowers the cancellation rate during his/her work, it might make the data inaccurate and the model might not be accurate.

Therefore, we should try to see whether to data collecting process might be accurate or not.

Bias and Fairness

Read Chapter 1: The Power Chapter of Data Feminism by Catherine D’Ignazio and Lauren Klein. Write a 4-6 sentence paragraph reflecting on this chapter. As you reflect, you might consider responding to these specific questions. We will also have a discussion about these questions in class on Thursday.

  • At the end of the “Matrix of Domination” section, they encourage us to “ask uncomfortable questions: who is doing the work of data science (and who is not)? Whose goals are prioritized in data science (and whose are not)? And who benefits from data science (and who is either overlooked or actively harmed)?” In general, how would you answer these questions? And why are they important?

  • Can you think of any examples of missing datasets, like those described in the “Data Science for Whom?” section? Or was there an example there that surprised you?

  • How did the examples in the “Data Science with Whose Interests and Goals?” section make you feel? What responsibility do companies have to prevent these things from occurring? Who is to blame?

After reading this chapter, I feel that it is extremely important to ask the question “who is doing the work of data science”. As if the data science jobs consist of looking at the data and trying to find the insights based on that, it will be really important that the data collection part as well as the data analysing process is being done carefully and without any bias. Personally, I have seen some examples of missing datasets, especially when someone tries to manipulate the data for their own purpose.

In my country, sometimes, to prove some certain ideas (such as the literacy rate), they will try to hide or eliminate parts of the data. However, we should ask the question that who is to blame? Is this the person who is inserting the data or is it due to the direction of the upper level class? We will not know the answer to be exact.

All and all, I feel that the best thing that we should do as an individual is try to acknowledge there are still biases in the world today and try the best to prevent it. Moreover, companies need to take steps to make things better and create an environment that will be less biased in the future.

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAjMicKYXV0aG9yOiAnRHVjIE5nbycKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKYGBge3IgZGF0YX0KaG90ZWxzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIwLzIwMjAtMDItMTEvaG90ZWxzLmNzdicpCmBgYAoKCldoZW4geW91IGZpbmlzaCB0aGUgYXNzaWdubWVudCwgcmVtb3ZlIHRoZSBgI2AgZnJvbSB0aGUgb3B0aW9ucyBjaHVuayBhdCB0aGUgdG9wLCBzbyB0aGF0IG1lc3NhZ2VzIGFuZCB3YXJuaW5ncyBhcmVuJ3QgcHJpbnRlZC4gSWYgeW91IGFyZSBnZXR0aW5nIGVycm9ycyBpbiB5b3VyIGNvZGUsIGFkZCBgZXJyb3IgPSBUUlVFYCBzbyB0aGF0IHRoZSBmaWxlIGtuaXRzLiBJIHdvdWxkIHJlY29tbWVuZCBub3QgcmVtb3ZpbmcgdGhlIGAjYCB1bnRpbCB5b3UgYXJlIGNvbXBsZXRlbHkgZmluaXNoZWQuCgojIyBQdXQgaXQgb24gR2l0SHViISAgICAgICAgCgpGcm9tIG5vdyBvbiwgR2l0SHViIHNob3VsZCBiZSBwYXJ0IG9mIHlvdXIgcm91dGluZSB3aGVuIGRvaW5nIGFzc2lnbm1lbnRzLiBJIHJlY29tbWVuZCBtYWtpbmcgaXQgcGFydCBvZiB5b3VyIHByb2Nlc3MgYW55dGltZSB5b3UgYXJlIHdvcmtpbmcgaW4gUiwgYnV0IEknbGwgbWFrZSB5b3Ugc2hvdyBpdCdzIHBhcnQgb2YgeW91ciBwcm9jZXNzIGZvciBhc3NpZ25tZW50cy4KCioqVGFzayoqOiBXaGVuIHlvdSBhcmUgZmluaXNoZWQgd2l0aCB0aGUgYXNzaWdubWVudCwgcG9zdCBhIGxpbmsgYmVsb3cgdG8gdGhlIEdpdEh1YiByZXBvIGZvciB0aGUgYXNzaWdubWVudC4gCgpIZXJlIGlzIG15IEdpdEh1YiByZXBvIGxpbms6IGh0dHBzOi8vZ2l0aHViLmNvbS9kdWNtaW5obmdvODcxL01hY2hpbmUtTGVhcm5pbm5nLUxhc3NvLiAKCiMjIE1hY2hpbmUgTGVhcm5pbmcgcmV2aWV3IGFuZCBpbnRybyB0byBgdGlkeW1vZGVsc2AKClJlYWQgdGhyb3VnaCBhbmQgZm9sbG93IGFsb25nIHdpdGggdGhlIFtNYWNoaW5lIExlYXJuaW5nIHJldmlldyB3aXRoIGFuIGludHJvIHRvIHRoZSBgdGlkeW1vZGVsc2AgcGFja2FnZV0oaHR0cHM6Ly9hZHZhbmNlZC1kcy1pbi1yLm5ldGxpZnkuYXBwL3Bvc3RzLzIwMjEtMDMtMTYtbWwtcmV2aWV3LykgcG9zdGVkIG9uIHRoZSBDb3Vyc2UgTWF0ZXJpYWxzIHBhZ2UuIAoKKipUYXNrcyoqOgoKMS4gUmVhZCBhYm91dCB0aGUgaG90ZWwgYm9va2luZyBkYXRhLCBgaG90ZWxzYCwgb24gdGhlIFtUaWR5IFR1ZXNkYXkgcGFnZV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjAvMjAyMC0wMi0xMS9yZWFkbWUubWQpIGl0IGNhbWUgZnJvbS4gVGhlcmUgaXMgYWxzbyBhIGxpbmsgdG8gYW4gYXJ0aWNsZSBmcm9tIHRoZSBvcmlnaW5hbCBhdXRob3JzLiBUaGUgb3V0Y29tZSB3ZSB3aWxsIGJlIHByZWRpY3RpbmcgaXMgY2FsbGVkIGBpc19jYW5jZWxlZGAuIAoKICAtIFdpdGhvdXQgZG9pbmcgYW55IGFuYWx5c2lzLCB3aGF0IGFyZSBzb21lIHZhcmlhYmxlcyB5b3UgdGhpbmsgbWlnaHQgYmUgcHJlZGljdGl2ZSBhbmQgd2h5PwogIApXaXRob3V0IGRvaW5nIGFueSBhbmFseXNpcywgSSB0aGluayBwcmV2aW91c19jYW5jZWxsYXRpb25zLCBwcmV2aW91c19ib29raW5nc19ub3RfY2FuY2VsZWQgYW5kIGRlcG9zaXRfdHlwZSBtaWdodCBiZSB0aGUgdGhyZWUgdmFyaWFibGVzIHRoYXQgbWlnaHQgYmUgcHJlZGljdGl2ZS4gVGhlIHJlYXNvbiBmb3IgdGhhdCBpcyBiZWNhc3VlIHByZXZpb3VzX2NhbmNlbGxhdGlvbnMgc2hvdyB0aGUgbnVtYmVyIG9mIHByZXZpb3VzIGJvb2tpbmdzIHRoYXQgd2VyZSBjYW5jZWxsZWQgYnkgdGhlIGN1c3RvbWVyIHByaW9yIHRvIHRoZSBjdXJyZW50IGJvb2tpbmcuIFRoZSBzYW1lIHRoaW5nIGNhbiBiZSBhcHBsaWVkIGZvciBwcmV2aW91c19ib29raW5nc19ub3RfY2FuY2VsZWQuIAoKQWRkaXRpb25hbGx5LCBmb3IgZGVwb3NpdF90eXBlLCBpZiB0aGUgcm9vbSBoYXMgYmVlbiBjaGFyZ2VkIChOb24gUmVmdW5kKSwgaXQgd2lsbCBiZSBtb3JlIGxpa2VseSB0aGF0IHRoZSByb29tIHdpbGwgbm90IGdldCBjYW5jZWxsZWQgYSBsb3Qgb2YgdGltZS4gCgogIF8gV2hhdCBhcmUgc29tZSBwcm9ibGVtcyB0aGF0IG1pZ2h0IGV4aXN0IHdpdGggdGhlIGRhdGE/IFlvdSBtaWdodCB0aGluayBhYm91dCBob3cgaXQgd2FzIGNvbGxlY3RlZCBhbmQgd2hvIGRpZCB0aGUgY29sbGVjdGluZy4gIAogIApTb21lIHByb2JsZW1zIG1pZ2h0IGV4aXN0IHdpdGggdGhlIGRhdGEgYXJlOiAKCi0gQXMgdGhlIGRhdGEgd2FzIG9idGFpbmVkIGRpcmVjdGx5IGZyb20gdGhlIGhvdGVsc+KAmSBQTVMgZGF0YWJhc2Vz4oCZIHNlcnZlcnMsIHRoZSBob3RlbCdzIHdvcmtlcnMgd2VyZSB0aGUgb25lIHdobyBjb21wbGV0ZSB0aGUgZGF0YS4gVGhlcmVmb3JlLCBpdCBtaWdodCBiZSBiaWFzZWQgYmFzZWQgb24gdGhlIHZpZXcgb2YgdGhlIGhvdGVsJ3Mgd29ya2Vycy4gCgogIC0gSWYgd2UgY29uc3RydWN0IGEgbW9kZWwsIHdoYXQgdHlwZSBvZiBjb25jbHVzaW9ucyB3aWxsIGJlIGFibGUgdG8gZHJhdyBmcm9tIGl0PyAgCgpJZiB3ZSBjcmVhdGUgYSBtb2RlbCwgd2UgY2FuIGZpbmQgd2hpY2ggdmFyaWFibGVzIGhhdmUgdGhlIGhpZ2hlc3QgaW1wYWN0IG9uIHRoZSBkZWNpc2lvbiBvZiBjYW5jZWxsaW5nIGEgYm9va2luZyBvciBub3QuIAoKMi4gQ3JlYXRlIHNvbWUgZXhwbG9yYXRvcnkgcGxvdHMgb3IgdGFibGUgc3VtbWFyaWVzIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuIEJlIHN1cmUgdG8gYWxzbyBleGFtaW5lIG1pc3NpbmcgdmFsdWVzIG9yIG90aGVyIGludGVyZXN0aW5nIHZhbHVlcy4gWW91IG1heSB3YW50IHRvIGFkanVzdCB0aGUgYGZpZy53aWR0aGAgYW5kIGBmaWcuaGVpZ2h0YCBpbiB0aGUgY29kZSBjaHVuayBvcHRpb25zLiAgCgpgYGB7ciBsaWJyYXJpZXN9CmxpYnJhcnkodGlkeXZlcnNlKSAgICAgICAgICMgZm9yIHJlYWRpbmcgaW4gZGF0YSwgZ3JhcGhpbmcsIGFuZCBjbGVhbmluZwpsaWJyYXJ5KHRpZHltb2RlbHMpICAgICAgICAjIGZvciBtb2RlbGluZyAuLi4gdGlkaWx5CmxpYnJhcnkodXNlbW9kZWxzKSAgICAgICAgICMgZm9yIHN1Z2dlc3Rpbmcgc3RlcF9YWFgoKSBmdW5jdGlvbnMKbGlicmFyeShnbG1uZXQpICAgICAgICAgICAgIyBmb3IgcmVndWxhcml6ZWQgcmVncmVzc2lvbiwgaW5jbHVkaW5nIExBU1NPCmxpYnJhcnkobmFuaWFyKSAgICAgICAgICAgICMgZm9yIGV4YW1pbmluZyBtaXNzaW5nIHZhbHVlcyAoTkFzKQpsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICAgICAjIGZvciBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KG1vZGVybmRpdmUpICAgICAgICAjIGZvciBLaW5nIENvdW50eSBob3VzaW5nIGRhdGEKbGlicmFyeSh2aXApICAgICAgICAgICAgICAgIyBmb3IgdmFyaWFibGUgaW1wb3J0YW5jZSBwbG90cwpsaWJyYXJ5KHJtYXJrZG93bikgICAgICAgICAjIGZvciBwYWdlZCB0YWJsZXMKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgIyBteSBmYXZvcml0ZSBnZ3Bsb3QyIHRoZW1lIDopCmBgYAoKRmlyc3QsIEkgd2lsbCBzZWUgdGhlIGZpcnN0IDUgcm93cyBvZiB0aGUgZGF0YSB0byBzZWUgaXRzIHZhcmlhYmxlczogCgpgYGB7cn0KaG90ZWxzICU+JSAKICBzbGljZSgxOjUpCmBgYAoKSW4gaGVyZSwgSSBjYW4gc2VlIHRoZSBkYXRhc2V0IGhhcyAzMiB2YXJpYWJsZXMgYW5kIHRoZSBvbmUgd2Ugd2lsbCBmb2N1cyBvbiBpbiB0aGlzIGFzc2lnbm1lbnQgaXMgaXNfY2FuY2VsZWQ6IFZhbHVlIGluZGljYXRpbmcgaWYgdGhlIGJvb2tpbmcgd2FzIGNhbmNlbGVkICgxKSBvciBub3QgKDApLgoKIyMjIDEuIFF1aWNrIGV4cGxvcmF0aW9uCgpBZnRlciBzZWVpbmcgdGhlIGRhdGFzZXQgZm9yIGEgYnJpZWYgbW9tZW50LCBJIHdpbGwgdGFrZSBhIHF1aWNrIGxvb2sgYXQgZGlzdHJpYnV0aW9ucyBvZiBhbGwgdGhlIHZhcmlhYmxlcyB0byBjaGVjayBmb3IgYW55dGhpbmcgaXJyZWd1bGFyLiAKClF1YW50aXRhdGl2ZSB2YXJpYWJsZXM6CgpgYGB7ciBleHBsX3F1YW50LCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQpob3RlbHMgJT4lIAogIHNlbGVjdCh3aGVyZShpcy5udW1lcmljKSkgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwKSArCiAgZmFjZXRfd3JhcCh2YXJzKHZhcmlhYmxlKSwgCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKV2hlbiBJIHNlZSB0aGUgaGlzdG9ncmFtIHBsb3RzIG9mIHRoZSB2YXJpYWJsZXMsIEkgY2FuIHNlZSB0aGF0IHRoZSBkYXRhIHdhcyBjb2xsZWN0ZWQgZm9yIDMgeWVhcnMgd2l0aCB0aGUgbWFqb3JpdHkgb2YgZGF0YSBjb21lcyBmcm9tIDIwMTYgYW5kIDIwMTcuIAoKVGhpbmdzIEkgaGF2ZSBub3RpY2VkIGZyb20gdGhlIGdyYXBoczoKCiogUmlnaHQgc2tld25lc3MgaW4gYGxlYWRfdGltZWAsIGBzdGF5c19pbl93ZWVrX25pZ2h0c2AgLS0+IGxvZyB0cmFuc2Zvcm0gaWYgdXNpbmcgbGluZWFyIHJlZ3Jlc3Npb24uCgoqIE1hbnkgMCdzIGluIGBjaGlsZHJlbmAsIGBkYXlzX2luX3dhaXRpbmdfbGlzdGAsIGBhZHJgLGBhZHVsdHNgLGBpc19yZXBlYXRlZF9ndWVzdGAgYW5kIGByZXF1aXJlZF9jYXJfcGFya2luZ19zcGFjZXNgIC0tPiBjcmVhdGUgaW5kaWNhdG9yIHZhcmlhYmxlcyBvZiBoYXZpbmcgdGhhdCBmZWF0dXJlIHZzLiBub3QsIGllLiBhIHZhcmlhYmxlIGNhbGxlZCBgY2hpbGRyZW5gIHdoZXJlIGEgYDBgIGluZGljYXRlcyBubyBiYXNlbWVudCAoYGNoaWxkcmVuYCA9IDApIGFuZCBhIGAxYCBpbmRpY2F0ZXMgYSBiYXNlbWVudCAoYGNoaWxkcmVuYCA+IDApLiAgCgpGb3IgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlczpJbiB0aGlzIGRhdGFzZXQsIHRoZXJlIGFyZSBubyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIAoKQWRkaXRpb25hbGx5LCB3ZSBtaWdodCB3YW50IHRvIHNlZSB0aGUgbnVtYmVyIG9mIGNhbmNlbGxlZCBib29raW5nIHZlcnN1cyB0aGUgbnVtYmVyIG9mIGJvb2tpbmcgbm90IGJlaW5nIGNhbmNlbGxlZC4gCgpgYGB7cn0KaG90ZWxzICU+JSAKICBjb3VudChpc19jYW5jZWxlZCkKYGBgCkZyb20gaGVyZSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBudW1iZXIgb2YgYm9va2luZ3MgdGhhdCBnb3QgY2FuY2VsZWQgaXMgY2xvc2UgdG8gdGhlIG51bWJlciBvZiBib29raW5ncyB0aGF0IGRpZCBub3QgZ2V0IGNhbmNlbGVkLiBUaGUgbnVtYmVyIG9mIGRhdGEgaXMgc3VmZmljaWVudCBlbm91Z2ggZm9yIHVzIHRvIGRvIGFueSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4gCgozLiBGaXJzdCwgd2Ugd2lsbCBkbyBhIGNvdXBsZSB0aGluZ3MgdG8gZ2V0IHRoZSBkYXRhIHJlYWR5LiAKCiogSSBkaWQgdGhlIGZvbGxvd2luZyBmb3IgeW91OiBtYWRlIG91dGNvbWUgYSBmYWN0b3IgKG5lZWRzIHRvIGJlIHRoYXQgd2F5IGZvciBsb2dpc3RpYyByZWdyZXNzaW9uKSwgbWFkZSBhbGwgY2hhcmFjdGVyIHZhcmlhYmxlcyBmYWN0b29ycywgcmVtb3ZlZCB0aGUgeWVhciB2YXJpYWJsZSBhbmQgc29tZSByZXNlcnZhdGlvbiBzdGF0dXMgdmFyaWFibGVzLCBhbmQgcmVtb3ZlZCBjYXNlcyB3aXRoIG1pc3NpbmcgdmFsdWVzIChub3QgTlVMTHMgYnV0IHRydWUgbWlzc2luZyB2YWx1ZXMpLgoKKiBZb3UgbmVlZCB0byBzcGxpdCB0aGUgZGF0YSBpbnRvIGEgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0LCBzdHJhdGlmeWluZyBvbiB0aGUgb3V0Y29tZSB2YXJpYWJsZSwgYGlzX2NhbmNlbGVkYC4gU2luY2Ugd2UgaGF2ZSBhIGxvdCBvZiBkYXRhLCBzcGxpdCB0aGUgZGF0YSA1MC81MCBiZXR3ZWVuIHRyYWluaW5nIGFuZCB0ZXN0LiBJIGhhdmUgYWxyZWFkeSBgc2V0LnNlZWQoKWAgZm9yIHlvdS4gQmUgc3VyZSB0byB1c2UgYGhvdGVsc19tb2RgIGluIHRoZSBzcGxpdHRpbmcuCgpgYGB7cn0KaG90ZWxzX21vZCA8LSBob3RlbHMgJT4lIAogIG11dGF0ZShpc19jYW5jZWxlZCA9IGFzLmZhY3Rvcihpc19jYW5jZWxlZCkpICU+JSAKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikpICU+JSAKICBzZWxlY3QoLWFycml2YWxfZGF0ZV95ZWFyLAogICAgICAgICAtcmVzZXJ2YXRpb25fc3RhdHVzLAogICAgICAgICAtcmVzZXJ2YXRpb25fc3RhdHVzX2RhdGUpICU+JSAKICBhZGRfbl9taXNzKCkgJT4lIAogIGZpbHRlcihuX21pc3NfYWxsID09IDApICU+JSAKICBzZWxlY3QoLW5fbWlzc19hbGwpCmBgYAoKYGBge3J9CnNldC5zZWVkKDQ5NCkKCiMgUmFuZG9tbHkgYXNzaWducyA3NSUgb2YgdGhlIGRhdGEgdG8gdHJhaW5pbmcuCmhvdGVsc19zcGxpdCA8LSBpbml0aWFsX3NwbGl0KGhvdGVsc19tb2QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3AgPSAuNSkKaG90ZWxzX3NwbGl0CiM8dHJhaW5pbmcvdGVzdGluZy90b3RhbD4KCmhvdGVsc190cmFpbmluZyA8LSB0cmFpbmluZyhob3RlbHNfc3BsaXQpCmhvdGVsc190ZXN0aW5nIDwtIHRlc3RpbmcoaG90ZWxzX3NwbGl0KQpgYGAKCjQuIEluIHRoaXMgbmV4dCBzdGVwLCB3ZSBhcmUgZ29pbmcgdG8gZG8gdGhlIHByZS1wcm9jZXNzaW5nLiBVc3VhbGx5LCBJIHdvbid0IHRlbGwgeW91IGV4YWN0bHkgd2hhdCB0byBkbyBoZXJlLCBidXQgZm9yIHlvdXIgZmlyc3QgZXhlcmNpc2UsIEknbGwgdGVsbCB5b3UgdGhlIHN0ZXBzLiAKCiogU2V0IHVwIHRoZSByZWNpcGUgd2l0aCBgaXNfY2FuY2VsZWRgIGFzIHRoZSBvdXRjb21lIGFuZCBhbGwgb3RoZXIgdmFyaWFibGVzIGFzIHByZWRpY3RvcnMgKEhJTlQ6IGB+LmApLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKKiBVc2UgYSBgc3RlcF9YWFgoKWAgZnVuY3Rpb24gb3IgZnVuY3Rpb25zIChJIHRoaW5rIHRoZXJlIGFyZSBvdGhlciB3YXlzIHRvIGRvIHRoaXMsIGJ1dCBJIGZvdW5kIGBzdGVwX211dGF0ZV9hdCgpYCBlYXNpZXN0KSB0byBjcmVhdGUgc29tZSBpbmRpY2F0b3IgdmFyaWFibGVzIGZvciB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczogYGNoaWxkcmVuYCwgYGJhYmllc2AsIGFuZCBgcHJldmlvdXNfY2FuY2VsbGF0aW9uc2AuIFNvLCB0aGUgbmV3IHZhcmlhYmxlIHNob3VsZCBiZSBhIDEgaWYgdGhlIG9yaWdpbmFsIGlzIG1vcmUgdGhhbiAwIGFuZCAwIG90aGVyd2lzZS4gTWFrZSBzdXJlIHlvdSBkbyB0aGlzIGluIGEgd2F5IHRoYXQgYWNjb3VudHMgZm9yIHZhbHVlcyB0aGF0IG1heSBiZSBsYXJnZXIgdGhhbiBhbnkgd2Ugc2VlIGluIHRoZSBkYXRhc2V0LiAgCiogRm9yIHRoZSBgYWdlbnRgIGFuZCBgY29tcGFueWAgdmFyaWFibGVzLCBtYWtlIG5ldyBpbmRpY2F0b3IgdmFyaWFibGVzIHRoYXQgYXJlIDEgaWYgdGhleSBoYXZlIGEgdmFsdWUgb2YgYE5VTExgIGFuZCAwIG90aGVyd2lzZS4gSSBhbHNvIHVzZWQgYHN0ZXBfbXV0YXRlX2F0KClgIGZvciB0aGlzLCBidXQgdGhlcmUncyBtb3JlIHdheXMgeW91IGNvdWxkIGRvIGl0LgoqIFVzZSBgZmN0X2x1bXBfbigpYCBpbnNpZGUgYHN0ZXBfbXV0YXRlKClgIHRvIGx1bXAgdG9nZXRoZXIgY291bnRyaWVzIHRoYXQgYXJlbid0IGluIHRoZSB0b3AgNSBtb3N0IG9jY3VycmluZy4gCiogSWYgeW91IHVzZWQgbmV3IG5hbWVzIGZvciBzb21lIG9mIHRoZSBuZXcgdmFyaWFibGVzIHlvdSBjcmVhdGVkLCB0aGVuIHJlbW92ZSBhbnkgdmFyaWFibGVzIHRoYXQgYXJlIG5vIGxvbmdlciBuZWVkZWQuIAoqIFVzZSBgc3RlcF9ub3JtYWxpemUoKWAgdG8gY2VudGVyIGFuZCBzY2FsZSBhbGwgdGhlIG5vbi1jYXRlZ29yaWNhbCBwcmVkaWN0b3IgdmFyaWFibGVzLiAoRG8gdGhpcyBCRUZPUkUgY3JlYXRpbmcgZHVtbXkgdmFyaWFibGVzLiBXaGVuIEkgdHJpZWQgdG8gZG8gaXQgYWZ0ZXIsIEkgcmFuIGludG8gYW4gZXJyb3IgLSBJJ20gc3RpbGwgW2ludmVzdGlnYXRpbmddKGh0dHBzOi8vY29tbXVuaXR5LnJzdHVkaW8uY29tL3QvdGlkeW1vZGVscy1zZWUtbm90ZXMtZXJyb3ItYnV0LW9ubHktd2l0aC1zdGVwLXh4eC1mdW5jdGlvbnMtaW4tYS1jZXJ0YWluLW9yZGVyLzExNTAwNikgd2h5LikKKiBDcmVhdGUgZHVtbXkgdmFyaWFibGVzIGZvciBhbGwgZmFjdG9ycy9jYXRlZ29yaWNhbCBwcmVkaWN0b3IgdmFyaWFibGVzIChtYWtlIHN1cmUgeW91IGhhdmUgYC1hbGxfb3V0Y29tZXMoKWAgaW4gdGhpcyBwYXJ0ISEpLiAgCiogVXNlIHRoZSBgcHJlcCgpYCBhbmQgYGp1aWNlKClgIGZ1bmN0aW9ucyB0byBhcHBseSB0aGUgc3RlcHMgdG8gdGhlIHRyYWluaW5nIGRhdGEganVzdCB0byBjaGVjayB0aGF0IGV2ZXJ5dGhpbmcgd2VudCBhcyBwbGFubmVkLgoKYGBge3J9CmhvdGVsX3JlY2lwZSA8LSByZWNpcGUoaXNfY2FuY2VsZWQgfiAuLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBob3RlbHNfdHJhaW5pbmcpICU+JSAKICBzdGVwX211dGF0ZV9hdChjaGlsZHJlbiwgYmFiaWVzLCBwcmV2aW91c19jYW5jZWxsYXRpb25zLAogICAgICAgICAgICAgICAgIGZuID0gfiBhcy5udW1lcmljKC4gPiAwKSkgJT4lCiAgc3RlcF9tdXRhdGVfYXQoYWdlbnQsIGNvbXBhbnksCiAgICAgICAgICAgICAgICAgZm4gPSB+IGFzLm51bWVyaWMoLiA9PSAiTlVMTCIpKSAlPiUKICBzdGVwX211dGF0ZShjb3VudHJ5LCAKICAgICAgICAgICAgICBjb3VudHJ5X2dycCA9IGZjdF9sdW1wX24oY291bnRyeSwgbiA9IDUpKSAlPiUgCiAgc3RlcF9ybShjb3VudHJ5KSAlPiUgCiAgc3RlcF9ub3JtYWxpemUoYWxsX3ByZWRpY3RvcnMoKSwKICAgICAgICAgICAgICAgICAtYWxsX25vbWluYWwoKSwKICAgICAgICAgICAgICAgICAtYWxsX291dGNvbWVzKCkpICU+JQogIHN0ZXBfZHVtbXkoYWxsX25vbWluYWwoKSwKICAgICAgICAgICAgIC1hbGxfb3V0Y29tZXMoKSkKICAKYGBgCgpgYGB7ciBhcHBseV9yZWNpcGV9CmhvdGVsX3JlY2lwZSAlPiUgCiAgcHJlcChob3RlbHNfdHJhaW5pbmcpICU+JQogICMgdXNpbmcgYmFrZShuZXdfZGF0YSA9IE5VTEwpIGdpdmVzIHNhbWUgcmVzdWx0IGFzIGp1aWNlKCkKICAjIGJha2UobmV3X2RhdGEgPSBOVUxMKQogIGp1aWNlKCkgCmBgYAoKNS4gSW4gdGhpcyBzdGVwIHdlIHdpbGwgc2V0IHVwIGEgTEFTU08gbW9kZWwgYW5kIHdvcmtmbG93LgoKKiBJbiBnZW5lcmFsLCB3aHkgd291bGQgd2Ugd2FudCB0byB1c2UgTEFTU08gaW5zdGVhZCBvZiByZWd1bGFyIGxvZ2lzdGljIHJlZ3Jlc3Npb24/IChISU5UOiB0aGluayBhYm91dCB3aGF0IGhhcHBlbnMgdG8gdGhlIGNvZWZmaWNpZW50cykuICAKCi0gTGFzc28gcmVndWxhcml6YXRpb25zIGFyZSBhbHNvIGtub3duIGFzIOKAmHNocmlua2FnZeKAmSBtZXRob2RzLCBiZWNhdXNlIHRoZXkgcmVkdWNlIG9yIHNocmluayB0aGUgY29lZmZpY2llbnRzIGluIHRoZSByZXN1bHRpbmcgcmVncmVzc2lvbi4gVGhpcyByZWR1Y2VzIHRoZSB2YXJpYW5jZSBpbiB0aGUgbW9kZWw6IGFzIGlucHV0IHZhcmlhYmxlcyBhcmUgY2hhbmdlZCwgdGhlIG1vZGVs4oCZcyBwcmVkaWN0aW9uIGNoYW5nZXMgbGVzcyB0aGFuIGl0IHdvdWxkIGhhdmUgd2l0aG91dCB0aGUgcmVndWxhcml6YXRpb24uIFdoaWxlIHVzaW5nIExBU1NPIG1vZGVsLCB3ZSBjYW4gYXZvaWQgb3ZlcmZpdCBieSByZWR1Y2luZyB0aGUgdmFyaWFuY2Ugb2YgYSBtb2RlbC4KCi0gTWVhbndoaWxlLCBsb2dpc3RpYyByZWdyZXNzaW9uIGRvZXMgbm90IHNocmluayB0aGUgdmFyaWFibGVzLCBzbyB3ZSB3aWxsIGhhdmUgYSBsb3Qgb2YgdmFyaWFibGVzIHRoYW4gbmVlZGVkLiAKCiogRGVmaW5lIHRoZSBtb2RlbCB0eXBlLCBzZXQgdGhlIGVuZ2luZSwgc2V0IHRoZSBgcGVuYWx0eWAgYXJndW1lbnQgdG8gYHR1bmUoKWAgYXMgYSBwbGFjZWhvbGRlciwgYW5kIHNldCB0aGUgbW9kZS4gIAoqIENyZWF0ZSBhIHdvcmtmbG93IHdpdGggdGhlIHJlY2lwZSBhbmQgbW9kZWwuICAKCjwhLS0gIyMjIFdoYXQgaXMgdGhlIHBlbmFsdHk6ICAtLT4KPCEtLSAtIFR5cGljYWxseSwgd2UgYXJlIGxvb2tpbmcgYXQgdGhlIHNxdWFyZSByZXNpZHVhbC4gIC0tPgo8IS0tIC0gTEFTU086IHNxdWFyZSByZXNpZHVhbCArIHBlbmFsdHkgdGVybSAoKSAtLT4KCgpgYGB7cn0KaG90ZWxfbGFzc29fbW9kIDwtIAogICMgRGVmaW5lIGEgbGFzc28gbW9kZWwgCiAgbG9naXN0aWNfcmVnKG1peHR1cmUgPSAxKSAlPiUgCiAgIyBTZXQgdGhlIGVuZ2luZSB0byAiZ2xtbmV0IiAKICBzZXRfZW5naW5lKCJnbG1uZXQiKSAlPiUgCiAgIyBUaGUgcGFyYW1ldGVycyB3ZSB3aWxsIHR1bmUuCiAgc2V0X2FyZ3MocGVuYWx0eSA9IHR1bmUoKSkgJT4lIAogICMgVXNlICJyZWdyZXNzaW9uIgogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpCmBgYAoKYGBge3J9CiMjIFNldCB1cCB0aGUgd29ya2Zsb3c6IApob3RlbF9sYXNzb193ZiA8LSAKICAjIFNldCB1cCB0aGUgd29ya2Zsb3cKICB3b3JrZmxvdygpICU+JSAKICAjIEFkZCB0aGUgcmVjaXBlCiAgYWRkX3JlY2lwZShob3RlbF9yZWNpcGUpICU+JSAKICAjIEFkZCB0aGUgbW9kZWxpbmcKICBhZGRfbW9kZWwoaG90ZWxfbGFzc29fbW9kKQoKaG90ZWxfbGFzc29fd2YKYGBgCgo2LiBJbiB0aGlzIHN0ZXAsIHdlJ2xsIHR1bmUgdGhlIG1vZGVsIGFuZCBmaXQgdGhlIG1vZGVsIHVzaW5nIHRoZSBiZXN0IHR1bmluZyBwYXJhbWV0ZXIgdG8gdGhlIGVudGlyZSB0cmFpbmluZyBkYXRhc2V0LgoKKiBDcmVhdGUgYSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBzYW1wbGUuIFdlJ2xsIHVzZSB0aGlzIGxhdGVyLiBJIGhhdmUgc2V0IHRoZSBzZWVkIGZvciB5b3UuICAKKiBVc2UgdGhlIGBncmlkX3JlZ3VsYXIoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGEgZ3JpZCBvZiAxMCBwb3RlbnRpYWwgcGVuYWx0eSBwYXJhbWV0ZXJzICh3ZSdyZSBrZWVwaW5nIHRoaXMgc29ydCBvZiBzbWFsbCBiZWNhdXNlIHRoZSBkYXRhc2V0IGlzIHByZXR0eSBsYXJnZSkuIFVzZSB0aGF0IHdpdGggdGhlIDUtZm9sZCBjdiBkYXRhIHRvIHR1bmUgdGhlIG1vZGVsLiAgCiogVXNlIHRoZSBgdHVuZV9ncmlkKClgIGZ1bmN0aW9uIHRvIGZpdCB0aGUgbW9kZWxzIHdpdGggZGlmZmVyZW50IHR1bmluZyBwYXJhbWV0ZXJzIHRvIHRoZSBkaWZmZXJlbnQgY3Jvc3MtdmFsaWRhdGlvbiBzZXRzLiAgCiogVXNlIHRoZSBgY29sbGVjdF9tZXRyaWNzKClgIGZ1bmN0aW9uIHRvIGNvbGxlY3QgYWxsIHRoZSBtZXRyaWNzIGZyb20gdGhlIHByZXZpb3VzIHN0ZXAgYW5kIGNyZWF0ZSBhIHBsb3Qgd2l0aCB0aGUgYWNjdXJhY3kgb24gdGhlIHktYXhpcyBhbmQgdGhlIHBlbmFsdHkgdGVybSBvbiB0aGUgeC1heGlzLiBQdXQgdGhlIHgtYXhpcyBvbiB0aGUgbG9nIHNjYWxlLiAgCiogVXNlIHRoZSBgc2VsZWN0X2Jlc3QoKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgYmVzdCB0dW5pbmcgcGFyYW1ldGVyLCBmaXQgdGhlIG1vZGVsIHVzaW5nIHRoYXQgdHVuaW5nIHBhcmFtZXRlciB0byB0aGUgZW50aXJlIHRyYWluaW5nIHNldCAoSElOVDogYGZpbmFsaXplX3dvcmtmbG93KClgIGFuZCBgZml0KClgKSwgYW5kIGRpc3BsYXkgdGhlIG1vZGVsIHJlc3VsdHMgdXNpbmcgYHB1bGxfd29ya2Zsb3dfZml0KClgIGFuZCBgdGlkeSgpYC4gQXJlIHRoZXJlIHNvbWUgdmFyaWFibGVzIHdpdGggY29lZmZpY2llbnRzIG9mIDA/CgpgYGB7cn0Kc2V0LnNlZWQoNDk0KSAjIGZvciByZXByb2R1Y2liaWxpdHkKCiNTcGxpdCB0aGUgZGF0YSBpbnRvIDUtZm9sZDogCmhvdGVsX2N2IDwtIHZmb2xkX2N2KGhvdGVsc190cmFpbmluZywgdiA9IDUpCgojIHBvdGVudGlhbCBwZW5hbHR5IHBhcmFtZXRlcnMKcGVuYWx0eV9ncmlkIDwtIGdyaWRfcmVndWxhcihwZW5hbHR5KCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gMTApCgpob3RlbF9sYXNzb190dW5lIDwtIAogIGhvdGVsX2xhc3NvX3dmICU+JSAKICB0dW5lX2dyaWQoCiAgICByZXNhbXBsZXMgPSBob3RlbF9jdiwKICAgIGdyaWQgPSBwZW5hbHR5X2dyaWQKICApCmBgYAoKYGBge3J9CmNvbGxlY3RfbWV0cmljcyhob3RlbF9sYXNzb190dW5lLCBzdW1tYXJpemUgPSBUUlVFKQpgYGAKYGBge3J9CiMjIyBIb3cgY2FuIHlvdSBzZWxlY3Qgb25seSAiYWNjdXJhY3kiIG5vdCByb2NfY3VydmUuIApjb2xsZWN0X21ldHJpY3MoaG90ZWxfbGFzc29fdHVuZSkgJT4lICAKICBmaWx0ZXIoLm1ldHJpYyA9PSAiYWNjdXJhY3kiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbG9nKHBlbmFsdHkpLCB5ID0gbWVhbikpICsgZ2VvbV9wb2ludCgpCmBgYAoKCmBgYHtyfQpob3RlbF9sYXNzb190dW5lICU+JSAKICBzaG93X2Jlc3QobWV0cmljID0gImFjY3VyYWN5IikKYGBgCgpgYGB7ciBiZXN0LXR1bmV9CiMgQmVzdCB0dW5pbmcgcGFyYW1ldGVyIGJ5IGFjY3VyYWN5CmJlc3RfcGFyYW0gPC0gaG90ZWxfbGFzc29fdHVuZSAlPiUgCiAgc2VsZWN0X2Jlc3QobWV0cmljID0gImFjY3VyYWN5IikKYmVzdF9wYXJhbQpgYGAKCmBgYHtyIHR1bmVfd2Z9CmhvdGVsX2xhc3NvX2ZpbmFsX3dmIDwtIGhvdGVsX2xhc3NvX3dmICU+JSAKICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X3BhcmFtKQpob3RlbF9sYXNzb19maW5hbF93ZgpgYGAKCk5vdyB3ZSBjb3VsZCBmaXQgdGhpcyB0byB0aGUgdHJhaW5pbmcgZGF0YSBhbmQgbG9vayBhdCB0aGUgcmVzdWx0aW5nIG1vZGVsLiAKCmBgYHtyIGxhc3NvX3RyYWlufQpob3RlbF9sYXNzb19maW5hbF9tb2QgPC0gaG90ZWxfbGFzc29fZmluYWxfd2YgJT4lIAogIGZpdChkYXRhID0gaG90ZWxzX3RyYWluaW5nKQoKaG90ZWxfbGFzc29fZmluYWxfbW9kICU+JSAKICBwdWxsX3dvcmtmbG93X2ZpdCgpICU+JSAKICB0aWR5KCkgCmBgYAoKSW4gaGVyZSwgSSBjYW4gc2VlIGEgZmV3IHRlcm1zIGhhdmUgY29lZmZpY2llbnRzIG9mIDAgKHN1Y2ggYXMgYXJyaXZhbF9kYXRlX21vbnRoX1NlcHRlbWJlciwgbWFya2V0X3NlZ21lbnRfVW5kZWZpbmVkLCBkaXN0cmlidXRpb25fY2hhbm5lbF9VbmRlZmluZWQpCgo3LiBOb3cgdGhhdCB3ZSBoYXZlIGEgbW9kZWwsIGxldCdzIGV2YWx1YXRlIGl0IGEgYml0IG1vcmUuIEFsbCB3ZSBoYXZlIGxvb2tlZCBhdCBzbyBmYXIgaXMgdGhlIGNyb3NzLXZhbGlkYXRlZCBhY2N1cmFjeSBmcm9tIHRoZSBwcmV2aW91cyBzdGVwLiAKCiogQ3JlYXRlIGEgdmFyaWFibGUgaW1wb3J0YW5jZSBncmFwaC4gV2hpY2ggdmFyaWFibGVzIHNob3cgdXAgYXMgdGhlIG1vc3QgaW1wb3J0YW50PyBBcmUgeW91IHN1cnByaXNlZD8gIAoKYGBge3IgdmlwfQojIFZpc3VhbGl6ZSB2YXJpYWJsZSBpbXBvcnRhbmNlCmhvdGVsX2xhc3NvX2ZpbmFsX21vZCAlPiUgCiAgcHVsbF93b3JrZmxvd19maXQoKSAlPiUgCiAgdmlwKCkKYGBgCgpJbiBoZXJlLCB0aGUgcmVzZXJ2ZWRfcm9vbV90eXBlX1AgY29tZXMgdXAgYXMgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmVzLCBmb2xsb3dlZCBieSBhc3NpZ25lZF9yb29tX3R5cGVfSSBhbmQgZGVwb3NpdF90eXBlX05vbi5SZWZ1bmQuIEZvciBtZSBwZXJzb25hbGx5LCBJIGFtIHByZXR0eSBzdXJwcmlzZWQgYWJvdXQgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2UuIFNpbmNlIEkgdGhvdWdodCB0aGF0IGRlcG9zaXQgdHlwZSBzaG91bGQgYmUgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlcywgaG93ZXZlciwgSSBhbSBmYWlybHkgc3VycHJpc2UgdGhhdCBpdCBpcyBvbmx5IGEgdG9wIDMgY2F0ZWdvcmllcy4gTW9yZW92ZXIsIEkgdGhvdWdodCBwcmV2aW91c19jYW5jZWxsYXRpb25zIG1pZ2h0IGJlIGFuIGltcG9ydGFudCB2YXJpYWJsZSwgaG93ZXZlciwgSSBkaWQgbm90IHNlZSB0aGF0IGluIHRoZSBtb2RlbC4gCgoqIFVzZSB0aGUgYGxhc3RfZml0KClgIGZ1bmN0aW9uIHRvIGZpdCB0aGUgZmluYWwgbW9kZWwgYW5kIHRoZW4gYXBwbHkgaXQgdG8gdGhlIHRlc3RpbmcgZGF0YS4gUmVwb3J0IHRoZSBtZXRyaWNzIGZyb20gdGhlIHRlc3RpbmcgZGF0YSB1c2luZyB0aGUgYGNvbGxlY3RfbWV0cmljcygpYCBmdW5jdGlvbi4gSG93IGRvIHRoZXkgY29tcGFyZSB0byB0aGUgY3Jvc3MtdmFsaWRhdGVkIG1ldHJpY3M/CgpgYGB7ciBsYXNzb190ZXN0fQojIEZpdCBtb2RlbCB3aXRoIGJlc3QgdHVuaW5nIHBhcmFtZXRlcihzKSB0byB0cmFpbmluZyBkYXRhIGFuZCBhcHBseSB0byB0ZXN0IGRhdGEKaG90ZWxfbGFzc29fdGVzdCA8LSBob3RlbF9sYXNzb19maW5hbF93ZiAlPiUgCiAgbGFzdF9maXQoaG90ZWxzX3NwbGl0KQoKIyBNZXRyaWNzIGZvciBtb2RlbCBhcHBsaWVkIHRvIHRlc3QgZGF0YQpob3RlbF9sYXNzb190ZXN0ICU+JSAKICBjb2xsZWN0X21ldHJpY3MoKQoKIyBUaGlzIG9uZSBpcyBub3QgdG90YWxseSByaWdodC4gCmNvbGxlY3RfbWV0cmljcyhob3RlbF9sYXNzb190dW5lKSAlPiUKICBmaWx0ZXIoLm1ldHJpYyA9PSAnYWNjdXJhY3knKQpgYGAKCkluIGhlcmUsIHdoZW4gd2UgbG9vayBhdCB0aGUgYWNjdXJhY3kgcmF0ZSwgdGhlIHRlc3RpbmcgZGF0YSdzIGFjY3VyYWN5IGlzIGZhaXJseSBjb25zaXN0ZW50IHdpdGggdGhlIHRyYWluaW5nIGRhdGEuIEZvciB0aGUgdGVzdGluZyBkYXRhLCBpdCBpcyBlc3RpbWF0ZWQgdGhhdCB0aGUgYWNjdXJhY3kgaXMgMC44MTM4MTQwLCBzbGlnaHRseSBsb3dlciB0aGFuIHRoZSB0cmFpbmluZyBkYXRhICgwLjgxNjEwOTEpLiBIb3dldmVyLCB0aGUgbnVtYmVyIGlzIGZhaXJseSBjb25zaXN0ZW50LiAKCiogVXNlIHRoZSBgY29sbGVjdF9wcmVkaWN0aW9ucygpYCBmdW5jdGlvbiB0byBmaW5kIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBhbmQgY2xhc3NlcyBmb3IgdGhlIHRlc3QgZGF0YS4gU2F2ZSB0aGlzIHRvIGEgbmV3IGRhdGFzZXQgY2FsbGVkIGBwcmVkc2AuIFRoZW4sIHVzZSB0aGUgYGNvbmZfbWF0KClgIGZ1bmN0aW9uIGZyb20gYGRpYWxzYCAocGFydCBvZiBgdGlkeW1vZGVsc2ApIHRvIGNyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXggc2hvd2luZyB0aGUgcHJlZGljdGVkIGNsYXNzZXMgdnMuIHRoZSB0cnVlIGNsYXNzZXMuIENvbXB1dGUgdGhlIHRydWUgcG9zaXRpdmUgcmF0ZSAoc2Vuc2l0aXZpdHkpLCB0cnVlIG5lZ2F0aXZlIHJhdGUgKHNwZWNpZmljaXR5KSwgYW5kIGFjY3VyYWN5LiBTZWUgdGhpcyBbV2lraXBlZGlhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db25mdXNpb25fbWF0cml4KSByZWZlcmVuY2UgaWYgeW91IChsaWtlIG1lKSB0ZW5kIHRvIGZvcmdldCB0aGVzZSBkZWZpbml0aW9ucy4gQWxzbyBrZWVwIGluIG1pbmQgdGhhdCBhICJwb3NpdGl2ZSIgaW4gdGhpcyBjYXNlIGlzIGEgY2FuY2VsbGF0aW9uICh0aG9zZSBhcmUgdGhlIDEncykuICAgIAoKYGBge3J9CnByZWRzIDwtIGNvbGxlY3RfcHJlZGljdGlvbnMoaG90ZWxfbGFzc29fdGVzdCkKcHJlZHMKYGBgCmBgYHtyfQpwcmVkcyAlPiUKICBjb25mX21hdCh0cnV0aCA9IGlzX2NhbmNlbGVkLCBlc3RpbWF0ZSA9IC5wcmVkX2NsYXNzKQpgYGAKCkZyb20gdGhpcyBjb25mdXNpb24gbWF0cml4OiAKCi0gVGhlIHRydWUgcG9zaXRpdmUgcmF0ZSB3aWxsIGJlOiAkJFxmcmFjezM0MjIxfXszNDIyMSsgNzczNH0gPSBcZnJhY3szNDIyMX17NDE5NTV9ID0gMC44MTYkJAoKLSBUaGUgdHJ1ZSBuZWdhdGl2ZSByYXRlIHdpbGwgYmU6ICQkXGZyYWN7MTQzNTh9ezE0MzU4ICsgMzM4MH0gPSBcZnJhY3sxNDM1OH17MTc3Mzh9ID0gMC44MDkkJAoKLSBUaGUgYWNjdXJhY3kgd2lsbCBiZTogJCRcZnJhY3szNDIyMSArIDE0MzU4fXszNDIyMSArIDc3MzQgKyAzMzgwICsgMTQzNTh9ID0gMC44MTM4ICQkCgoqIFVzZSB0aGUgYHByZWRzYCBkYXRhc2V0IHlvdSBqdXN0IGNyZWF0ZWQgdG8gY3JlYXRlIGEgZGVuc2l0eSBwbG90IG9mIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBvZiBjYW5jZWxpbmcgKHRoZSB2YXJpYWJsZSBpcyBjYWxsZWQgYC5wcmVkXzFgKSwgZmlsbGluZyBieSBgaXNfY2FuY2VsZWRgLiBVc2UgYW4gYGFscGhhID0gLjVgIGFuZCBgY29sb3IgPSBOQWAgaW4gdGhlIGBnZW9tX2RlbnNpdHkoKWAuIAoKYGBge3J9CnByZWRzICU+JQogIGdncGxvdChhZXMoeCA9IC5wcmVkXzEsIGZpbGwgPSBpc19jYW5jZWxlZCkpICsgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41LCBjb2xvciA9IE5BKQpgYGAKCkFuc3dlciB0aGVzZSBxdWVzdGlvbnM6IAoKYS4gV2hhdCB3b3VsZCB0aGlzIGdyYXBoIGxvb2sgbGlrZSBmb3IgYSBtb2RlbCB3aXRoIGFuIGFjY3VyYWN5IHRoYXQgd2FzIGNsb3NlIHRvIDE/ICAKCkkgYmVsaWV2ZSB0aGF0IGEgbW9kZWwgd2l0aCBhbiBhY2N1cmFjeSB0aGF0IHdhcyBjbG9zZSB0byAxIHdpbGwgaGF2ZSB0d28gZGlzdGluY3QgcGFydHMgYW5kIHdpbGwgbm90IGhhdmUgbWFueSBvdmVybGFwIHBvaW50cy5Nb3Jlb3ZlciwgdGhlIHR3byBwYXJ0cyB3aWxsIGJlIHJlYWxseSBsZWZ0LXNrZXdlZCBhbmQgcmlnaHQtc2tld2VkLiBUaGUgcmVhc29uIGZvciB0aGF0IHdpbGwgYmU6IGlmIHRoZSBkYXRhc2V0IHNhaWQgdGhhdCB0aGlzIHBlcnNvbiBjYW5jZWxlZCBoaXMgYm9va2luZyAoaXNfY2FuY2VsZWQgPSAxKSBhbmQgdGhlIGRhdGFzZXQgY29ycmVjdGx5IHByZWRpY3QgdGhhdCwgdGhlIHByZWRpY3Rpb24gd2lsbCBiZSBjbG9zZSB0byAxLiBXaXRoIHRoYXQsIHRoZXJlIHdpbGwgaGF2ZSBsZXNzIGRhdGFzZXQgd2l0aCAucHJlZF8xIDwgMC41IHdoaWxlIGlzX2NhbmNlbGVkID0gMSBhbmQgdmljZSB2ZXJzYSBmb3IgaXNfY2FuY2VsZWQgPSAwLiBXaXRoIHRoYXQsIHRoZSBncmFwaCB3aWxsIGJlIHNwbGl0IGludG8gdHdvIHBhcnRzOiBvbmUgcmlnaHQtc2tld2VkIHJlZCBwYXJ0IGFuZCBvbmUgbGVmdC1za2V3ZWQgYmx1ZSBwYXJ0LiAKCmIuIE91ciBwcmVkaWN0aW9ucyBhcmUgY2xhc3NpZmllZCBhcyBjYW5jZWxlZCBpZiB0aGVpciBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgb2YgY2FuY2VsaW5nIGlzIGdyZWF0ZXIgdGhhbiAuNS4gSWYgd2Ugd2FudGVkIHRvIGhhdmUgYSBoaWdoIHRydWUgcG9zaXRpdmUgcmF0ZSwgc2hvdWxkIHdlIG1ha2UgdGhlIGN1dG9mZiBmb3IgcHJlZGljdGVkIGFzIGNhbmNlbGVkIGhpZ2hlciBvciBsb3dlciB0aGFuIC41PyAgCgpBcyB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIGNhbiBiZSBjYWxjdWxhdGVkIGFzIFRydWUgUG9zaXRpdmUgLyAoVHJ1ZSBQb3NpdGl2ZSArIEZhbHNlIE5lZ2F0aXZlKS4gClRoZXJlZm9yZSwgdG8gbWFrZSB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIGhpZ2hlciwgd2Ugc2hvdWxkIG1ha2UgdGhlIGN1dG9mZiBoaWdoZXIsIHdoaWNoIHRoZW4gbWFrZXMgdGhlIHByZWRpY3Rpb24gbW9yZSBhY2N1cmF0ZS4gCgpjLiBXaGF0IGhhcHBlbnMgdG8gdGhlIHRydWUgbmVnYXRpdmUgcmF0ZSBpZiB3ZSB0cnkgdG8gZ2V0IGEgaGlnaGVyIHRydWUgcG9zaXRpdmUgcmF0ZT8gCgpJZiB3ZSB0cnkgdG8gZ2V0IGEgaGlnaGVyIHRydWUgcG9zaXRpdmUgcmF0ZSwgd2UgbmVlZCB0byBpbmNyZWFzZSB0aGUgY3V0IG9mZiByYXRlLiBBcyB3ZSBoYXZlIGxlc3MgcG9zdGl2ZSBjYXNlcywgd2Ugd2lsbCBoYXZlIG1vcmUgbmVnYXRpdmUgY2FzZXMuIFdpdGggdGhhdCwgd2Ugd2lsbCBiZSBtb3JlIGxpa2VseSB0byBnZXQgYSBmYWxzZSBuZWdhdGl2ZSBjYXNlcyBhbmQgdGhlIHRydWUgbmVnYXRpdmUgcmF0ZSB3aWxsIGRlY3JlYXNlLiAKCjguIExldCdzIHNheSB0aGF0IHRoaXMgbW9kZWwgaXMgZ29pbmcgdG8gYmUgYXBwbGllZCB0byBib29raW5ncyAxNCBkYXlzIGluIGFkdmFuY2Ugb2YgdGhlaXIgYXJyaXZhbCBhdCBlYWNoIGhvdGVsLCBhbmQgc29tZW9uZSB3aG8gd29ya3MgZm9yIHRoZSBob3RlbCB3aWxsIG1ha2UgYSBwaG9uZSBjYWxsIHRvIHRoZSBwZXJzb24gd2hvIG1hZGUgdGhlIGJvb2tpbmcuIER1cmluZyB0aGlzIHBob25lIGNhbGwsIHRoZXkgd2lsbCB0cnkgdG8gYXNzdXJlIHRoYXQgdGhlIHBlcnNvbiB3aWxsIGJlIGtlZXBpbmcgdGhlaXIgcmVzZXJ2YXRpb24gb3IgdGhhdCB0aGV5IHdpbGwgYmUgY2FuY2VsaW5nIGluIHdoaWNoIGNhc2UgdGhleSBjYW4gZG8gdGhhdCBub3cgYW5kIHN0aWxsIGhhdmUgdGltZSB0byBmaWxsIHRoZSByb29tLiBIb3cgc2hvdWxkIHRoZSBob3RlbCBnbyBhYm91dCBkZWNpZGluZyB3aG8gdG8gY2FsbD8gSG93IGNvdWxkIHRoZXkgbWVhc3VyZSB3aGV0aGVyIGl0IHdhcyB3b3J0aCB0aGUgZWZmb3J0IHRvIGRvIHRoZSBjYWxsaW5nPyBDYW4geW91IHRoaW5rIG9mIGFub3RoZXIgd2F5IHRoZXkgbWlnaHQgdXNlIHRoZSBtb2RlbD8gCgpGb3IgdGhlIG1vZGVsLCBhY2NvcmRpbmcgdG8gdGhlIGltcG9ydGFuY2UgdmFyaWFibGVzIGdyYXBoLCB0aGUgaG90ZWwgc2hvdWxkIGNhbGwgdGhlIHBlcnNvbiB3aG8gYm9va3MgdGhlIHJvb20gdHlwZSBQIG9yIGhhcyBiZWVuIGFzc2lnbmVkIHRvIHJvb20gSS4gQWRkaXRpb25hbGx5LCB0aGV5IHNob3VsZCBjYWxsIHRoZSBwZXJzb24gd2hvIGJvb2tzIHRoZSByb29tIGlzIG5vdCByZWZ1bmRhYmxlLiAKClRoZXkgY291bGQgbWVhc3VyZSB3aG8gdG8gY2FsbCBiYXNlZCBvbiB0aGUgcmF0ZSBvZiB0aGF0IHJvb20gd2FzIGJlaW5nIGNhbmNlbGxlZCBhcyB3ZWxsIGFzIGhvdyBsaWtlbHkgdGhhdCB3aWxsIGhhcHBlbi4gRm9yIGV4YW1wbGUsIHRoZXkgY2FuIGxvb2sgaW50byB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSB0byBkZWNpZGUgd2hpY2ggdmFyaWFibGVzIHdpbGwgYmUgaW1wb3J0YW50LiBPbmNlIHRoZXkgY29tYmluZSB0aGVpciBwcmV2aW91cyB1bmRlcnRhbmRpbmcgYWJvdXQgdGhlIGRlbW9ncmFwaGljIGFzIHdlbGwgYXMgdGhlIHJlc3VsdCBmcm9tIHRoZSBtb2RlbCwgdGhleSBjYW4gZmluZCBvdXQgd2hvIHNob3VsZCB0aGV5IGNhbGwuIAoKQW5vdGhlciB3YXkgdGhleSBjYW4gdXNlIHRoZSBtb2RlbCBpcyB0aGF0IHRoZXkgY2FuIGZvY3VzIG1vcmUgb24gc29tZSBrZXkgcGFydHMgKGZvciBleGFtcGxlOiBvbiB0aGUgcmVzZXJ2ZWRfcm9vbSBQLCBHIG9yIEYgcmF0aGVyIHRoYW4gQSxCIG9yIEMpLiBXaXRoIHRoZXNlIG1vZGVscyBhbmQgaXRzIGF0dHJpYnV0aW9ucyB0byB0aGUgY2FuY2VsbGF0aW9uIG51bWJlciwgdGhleSBjYW4gaW1wbGVtZW50IHRoZSB2YXJpYWJsZXMgdGhhdCBoYXZlIHBvc2l0aXZlIGF0dHJpYnV0ZXMgc3VjaCBhcyBsZWFkX3RpbWUgb3Igc3RheV9pbl93ZWVrX25pZ2h0cyBmb3IgaW5zdGFuY2UgdG8gY3JlYXRlIG5ldyBydWxlcy4gRm9yIGV4YW1wbGUsIHRoZXkgY2FuIGxvd2VyIHRoZSByYXRlIHRvIHN0YXkgaW4gdGhlIGhvdGVsIGR1cmluZyB3ZWVrIG5pZ2h0cyBzbyB0aGF0IHRoZXkgY2FuIGF0dHJhY3QgbW9yZSB2aXNpdG9ycyBkdXJpbmcgdGhhdCB0aW1lLiAKCjkuIEhvdyBtaWdodCB5b3UgZ28gYWJvdXQgcXVlc3Rpb25pbmcgYW5kIGV2YWx1YXRpbmcgdGhlIG1vZGVsIGluIHRlcm1zIG9mIGZhaXJuZXNzPyBBcmUgdGhlcmUgYW55IHF1ZXN0aW9ucyB5b3Ugd291bGQgbGlrZSB0byBhc2sgb2YgdGhlIHBlb3BsZSB3aG8gY29sbGVjdGVkIHRoZSBkYXRhPyAKCkluIGhlcmUsIHNpbmNlIHRoZSBwZXJzb24gd2hvIGNyZWF0ZXMgdGhlIGRhdGEgd29ya3MgaW4gdGhlIGhvdGVsLCBvbmUgbWlnaHQgYXNrIHdoZXRoZXIgaGUvc2hlIGNvbGxlY3RzIHRoZSBkYXRhIGFjY3VyYXRlbHkgb3Igbm90LiBJZiBoZS9zaGUgbG93ZXJzIHRoZSBjYW5jZWxsYXRpb24gcmF0ZSBkdXJpbmcgaGlzL2hlciB3b3JrLCBpdCBtaWdodCBtYWtlIHRoZSBkYXRhIGluYWNjdXJhdGUgYW5kIHRoZSBtb2RlbCBtaWdodCBub3QgYmUgYWNjdXJhdGUuIAoKVGhlcmVmb3JlLCB3ZSBzaG91bGQgdHJ5IHRvIHNlZSB3aGV0aGVyIHRvIGRhdGEgY29sbGVjdGluZyBwcm9jZXNzIG1pZ2h0IGJlIGFjY3VyYXRlIG9yIG5vdC4gCgoKCiMjIEJpYXMgYW5kIEZhaXJuZXNzCgpSZWFkIFtDaGFwdGVyIDE6IFRoZSBQb3dlciBDaGFwdGVyXShodHRwczovL2RhdGEtZmVtaW5pc20ubWl0cHJlc3MubWl0LmVkdS9wdWIvdmk4b2J4aDcvcmVsZWFzZS80KSBvZiBEYXRhIEZlbWluaXNtIGJ5IENhdGhlcmluZSBEJ0lnbmF6aW8gYW5kIExhdXJlbiBLbGVpbi4gV3JpdGUgYSA0LTYgc2VudGVuY2UgcGFyYWdyYXBoIHJlZmxlY3Rpbmcgb24gdGhpcyBjaGFwdGVyLiBBcyB5b3UgcmVmbGVjdCwgeW91IG1pZ2h0IGNvbnNpZGVyIHJlc3BvbmRpbmcgdG8gdGhlc2Ugc3BlY2lmaWMgcXVlc3Rpb25zLiBXZSB3aWxsIGFsc28gaGF2ZSBhIGRpc2N1c3Npb24gYWJvdXQgdGhlc2UgcXVlc3Rpb25zIGluIGNsYXNzIG9uIFRodXJzZGF5LgoKKiBBdCB0aGUgZW5kIG9mIHRoZSAiTWF0cml4IG9mIERvbWluYXRpb24iIHNlY3Rpb24sIHRoZXkgZW5jb3VyYWdlIHVzIHRvICJhc2sgdW5jb21mb3J0YWJsZSBxdWVzdGlvbnM6IHdobyBpcyBkb2luZyB0aGUgd29yayBvZiBkYXRhIHNjaWVuY2UgKGFuZCB3aG8gaXMgbm90KT8gV2hvc2UgZ29hbHMgYXJlIHByaW9yaXRpemVkIGluIGRhdGEgc2NpZW5jZSAoYW5kIHdob3NlIGFyZSBub3QpPyBBbmQgd2hvIGJlbmVmaXRzIGZyb20gZGF0YSBzY2llbmNlIChhbmQgd2hvIGlzIGVpdGhlciBvdmVybG9va2VkIG9yIGFjdGl2ZWx5IGhhcm1lZCk/IiBJbiBnZW5lcmFsLCBob3cgd291bGQgeW91IGFuc3dlciB0aGVzZSBxdWVzdGlvbnM/IEFuZCB3aHkgYXJlIHRoZXkgaW1wb3J0YW50PyAgCgoKKiBDYW4geW91IHRoaW5rIG9mIGFueSBleGFtcGxlcyBvZiBtaXNzaW5nIGRhdGFzZXRzLCBsaWtlIHRob3NlIGRlc2NyaWJlZCBpbiB0aGUgIkRhdGEgU2NpZW5jZSBmb3IgV2hvbT8iIHNlY3Rpb24/IE9yIHdhcyB0aGVyZSBhbiBleGFtcGxlIHRoZXJlIHRoYXQgc3VycHJpc2VkIHlvdT8gIAoKCiogSG93IGRpZCB0aGUgZXhhbXBsZXMgaW4gdGhlICJEYXRhIFNjaWVuY2Ugd2l0aCBXaG9zZSBJbnRlcmVzdHMgYW5kIEdvYWxzPyIgc2VjdGlvbiBtYWtlIHlvdSBmZWVsPyBXaGF0IHJlc3BvbnNpYmlsaXR5IGRvIGNvbXBhbmllcyBoYXZlIHRvIHByZXZlbnQgdGhlc2UgdGhpbmdzIGZyb20gb2NjdXJyaW5nPyBXaG8gaXMgdG8gYmxhbWU/CgpBZnRlciByZWFkaW5nIHRoaXMgY2hhcHRlciwgSSBmZWVsIHRoYXQgaXQgaXMgZXh0cmVtZWx5IGltcG9ydGFudCB0byBhc2sgdGhlIHF1ZXN0aW9uICJ3aG8gaXMgZG9pbmcgdGhlIHdvcmsgb2YgZGF0YSBzY2llbmNlIi4gQXMgaWYgdGhlIGRhdGEgc2NpZW5jZSBqb2JzIGNvbnNpc3Qgb2YgbG9va2luZyBhdCB0aGUgZGF0YSBhbmQgdHJ5aW5nIHRvIGZpbmQgdGhlIGluc2lnaHRzIGJhc2VkIG9uIHRoYXQsIGl0IHdpbGwgYmUgcmVhbGx5IGltcG9ydGFudCB0aGF0IHRoZSBkYXRhIGNvbGxlY3Rpb24gcGFydCBhcyB3ZWxsIGFzIHRoZSBkYXRhIGFuYWx5c2luZyBwcm9jZXNzIGlzIGJlaW5nIGRvbmUgY2FyZWZ1bGx5IGFuZCB3aXRob3V0IGFueSBiaWFzLiBQZXJzb25hbGx5LCBJIGhhdmUgc2VlbiBzb21lIGV4YW1wbGVzIG9mIG1pc3NpbmcgZGF0YXNldHMsIGVzcGVjaWFsbHkgd2hlbiBzb21lb25lIHRyaWVzIHRvIG1hbmlwdWxhdGUgdGhlIGRhdGEgZm9yIHRoZWlyIG93biBwdXJwb3NlLiAKCkluIG15IGNvdW50cnksIHNvbWV0aW1lcywgdG8gcHJvdmUgc29tZSBjZXJ0YWluIGlkZWFzIChzdWNoIGFzIHRoZSBsaXRlcmFjeSByYXRlKSwgdGhleSB3aWxsIHRyeSB0byBoaWRlIG9yIGVsaW1pbmF0ZSBwYXJ0cyBvZiB0aGUgZGF0YS4gSG93ZXZlciwgd2Ugc2hvdWxkIGFzayB0aGUgcXVlc3Rpb24gdGhhdCB3aG8gaXMgdG8gYmxhbWU/IElzIHRoaXMgdGhlIHBlcnNvbiB3aG8gaXMgaW5zZXJ0aW5nIHRoZSBkYXRhIG9yIGlzIGl0IGR1ZSB0byB0aGUgZGlyZWN0aW9uIG9mIHRoZSB1cHBlciBsZXZlbCBjbGFzcz8gV2Ugd2lsbCBub3Qga25vdyB0aGUgYW5zd2VyIHRvIGJlIGV4YWN0LiAKCkFsbCBhbmQgYWxsLCBJIGZlZWwgdGhhdCB0aGUgYmVzdCB0aGluZyB0aGF0IHdlIHNob3VsZCBkbyBhcyBhbiBpbmRpdmlkdWFsIGlzIHRyeSB0byBhY2tub3dsZWRnZSB0aGVyZSBhcmUgc3RpbGwgYmlhc2VzIGluIHRoZSB3b3JsZCB0b2RheSBhbmQgdHJ5IHRoZSBiZXN0IHRvIHByZXZlbnQgaXQuIE1vcmVvdmVyLCBjb21wYW5pZXMgbmVlZCB0byB0YWtlIHN0ZXBzIHRvIG1ha2UgdGhpbmdzIGJldHRlciBhbmQgY3JlYXRlIGFuIGVudmlyb25tZW50IHRoYXQgd2lsbCBiZSBsZXNzIGJpYXNlZCBpbiB0aGUgZnV0dXJlLiAKCg==